import json
from typing import Optional
import os
import re
import math

import pandas as pd

def read_city(file_path: Optional[str] = None) -> dict:
    """
    读取百度地图开放平台提供的城市中心经纬度

    @param file_path: 文件路径名
    @return: 所有城市中心点经纬度字典
    """
    if file_path is None:
        file_path = os.path.join(os.path.dirname(__file__), 'BaiduMap_cityCenter.txt')
    
    with open(file_path, 'r', encoding='GBK') as f:
        txt = f.read()

    # 原始数据并不是标准的json文件
    ##  给键名添上双引号
    txt = re.sub(r'([{|,])([a-z]*?):', r'\g<1>"\g<2>":', txt)
    ## 去掉尾巴上的分号
    txt = txt[ : -2]

    city_data = json.loads(txt)
    result = {}
    # 递归取出所有城市的经纬度
    for key, value in city_data.items():
        # 处理直辖市
        if key == 'municipalities':
            for item in value:
                result[item['n']] = split_lng_lat(item['g'])
        
        # 处理省份
        if key == 'provinces':
            for province in value:
                for city in province['cities']:
                    result[city['n']] = split_lng_lat(city['g'])
    
    return result


def split_lng_lat(raw_str: str) -> list:
    """
    将百度开放平台当中的的经纬度字符串切割成经纬度列表

    @param raw_str: 原始经纬度字符串
    @return: 经纬度列表
    """
    return [float(s) for s in re.split('[,|]', raw_str)[0:2]]


def save_cities(city_data: dict, target_path: Optional[str] = None) -> None:
    """
    将处理好的数据保存为csv格式文件

    @param city_data: 使用read_city函数处理好的字典
    @param target_path: csv格式文件的目标路径
    """
    if target_path is None:
        target_path = os.path.join(os.path.dirname(__file__), 'cities_lng_lat.csv')

    csv = 'city,longitude,latitude\n'
    for key, value in city_data.items():
        csv += f'{key},{value[0]},{value[1]}\n'
    with open(target_path, 'w', encoding='utf-8') as f:
        f.write(csv)


def calculate_distance(origin: list, destination: list) -> float:
    """
    根据输入的两个经纬度计算两地之间的距离

    @param origin: 起点经纬度，经度在前，维度在后
    @param destination: 终点经纬度，经度在前，维度在后

    @return: 两个经纬度之间的距离
    """
    # ATTENTION: 该函数仅用于计算中国各地市之间的距离，各地市的经纬度都是东经北纬
    # reference: https://www.zhihu.com/question/265407371/answer/437817362

    def rad(x: float) -> float:
        """
        角度转弧度

        @param x: 角度
        @return: 弧度
        """
        return x * math.pi / 180

    rad_lat_1 = rad(origin[1]) # 起点纬度
    rad_lat_2 = rad(destination[1]) # 终点纬度
    a = rad_lat_1 - rad_lat_2 # 纬度差
    b = rad(origin[0]) - rad(destination[0]) # 经度差

    s = 2 * math.asin(math.sqrt(math.pow(math.sin(a / 2), 2) + math.cos(rad_lat_1) * math.cos(rad_lat_2) * math.pow(math.sin(b / 2), 2)))
    return 6378.137 * s


def calculate_all(city_data: dict) -> pd.DataFrame:
    """
    计算所有地市间的经纬度距离

    @param city_data: 使用read_city处理好的地市字典
    """
    origin = []
    destination = []
    cities = list(city_data.keys())
    for i in range(len(cities)):
        for j in range(i + 1, len(cities)):
            origin.append(cities[i])
            destination.append(cities[j])

    result = pd.DataFrame({'origin': origin, 'destination': destination, 'lng_lat_distance': [0.0]*len(origin)})
    for index in range(len(result)):
        result.at[index, 'lng_lat_distance'] = calculate_distance(city_data[result.at[index, 'origin']], city_data[result.at[index, 'destination']])
    return result


if __name__ == "__main__":
    city_list = read_city()
    df = calculate_all(city_list)